package main

import (
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"

	"gopkg.in/yaml.v3"
)

type ServerConfig struct {
	Name          string `yaml:"name"`
	URL           string `yaml:"url"`
	CheckInterval string `yaml:"check_interval"`
	Enabled       bool   `yaml:"enabled"`
}

type ServiceConfig struct {
	Name          string   `yaml:"name"`
	Executable    string   `yaml:"executable"`
	WorkDir       string   `yaml:"work_dir"`
	Args          []string `yaml:"args"`
	CheckInterval string   `yaml:"check_interval"`
	ProcessName   string   `yaml:"process_name"`
	MaxFailures   int      `yaml:"max_failures"`
	Enabled       bool     `yaml:"enabled"`
}

type EmailConfig struct {
	SMTPServer string   `yaml:"smtp_server"`
	SMTPPort   int      `yaml:"smtp_port"`
	Username   string   `yaml:"username"`
	Password   string   `yaml:"password"`
	From       string   `yaml:"from"`
	To         []string `yaml:"to"`
}

type Config struct {
	Services []ServiceConfig `yaml:"services"`
	Servers  []ServerConfig  `yaml:"servers"`
	Email    EmailConfig     `yaml:"email"`
}

// ParseDuration 解析时间间隔字符串，支持 s(秒)、m(分钟)、h(小时)
func ParseDuration(duration string) (time.Duration, error) {
	if duration == "" {
		return 0, fmt.Errorf("时间间隔不能为空")
	}

	// 使用正则表达式匹配时间格式
	re := regexp.MustCompile(`^(\d+)([smh])$`)
	matches := re.FindStringSubmatch(duration)
	if len(matches) != 3 {
		return 0, fmt.Errorf("时间格式无效，应为数字+单位(s/m/h)，如: 30s, 5m, 2h")
	}

	value, err := strconv.Atoi(matches[1])
	if err != nil {
		return 0, fmt.Errorf("时间数值无效: %v", err)
	}

	unit := matches[2]
	switch unit {
	case "s":
		return time.Duration(value) * time.Second, nil
	case "m":
		return time.Duration(value) * time.Minute, nil
	case "h":
		return time.Duration(value) * time.Hour, nil
	default:
		return 0, fmt.Errorf("不支持的时间单位: %s", unit)
	}
}

func (c *Config) Validate() error {
	// 验证至少有一种监控配置
	if len(c.Services) == 0 && len(c.Servers) == 0 {
		return fmt.Errorf("配置文件中没有定义任何服务或服务器")
	}

	// 验证服务配置
	for i, service := range c.Services {
		if service.Name == "" {
			return fmt.Errorf("服务 #%d 没有名称", i+1)
		}
		if service.Executable == "" {
			return fmt.Errorf("服务 %s 没有可执行文件路径", service.Name)
		}
		if service.ProcessName == "" {
			return fmt.Errorf("服务 %s 没有进程名称", service.Name)
		}
		if service.MaxFailures <= 0 {
			return fmt.Errorf("服务 %s 的最大失败次数必须大于0", service.Name)
		}

		// 验证检查间隔
		if _, err := ParseDuration(service.CheckInterval); err != nil {
			return fmt.Errorf("服务 %s 的检查间隔无效: %v", service.Name, err)
		}
	}

	// 验证服务器配置
	for i, server := range c.Servers {
		if server.Name == "" {
			return fmt.Errorf("服务器 #%d 没有名称", i+1)
		}
		if server.URL == "" {
			return fmt.Errorf("服务器 %s 没有URL", server.Name)
		}
		if !strings.HasPrefix(server.URL, "http://") && !strings.HasPrefix(server.URL, "https://") {
			return fmt.Errorf("服务器 %s 的URL必须以http://或https://开头", server.Name)
		}

		// 验证检查间隔
		if duration, err := ParseDuration(server.CheckInterval); err != nil {
			return fmt.Errorf("服务器 %s 的检查间隔无效: %v", server.Name, err)
		} else if duration < 30*time.Second {
			return fmt.Errorf("服务器 %s 的检查间隔不能小于30秒", server.Name)
		}
	}

	// 验证邮件配置
	if c.Email.SMTPServer == "" {
		return fmt.Errorf("未配置SMTP服务器")
	}
	if c.Email.SMTPPort <= 0 || c.Email.SMTPPort > 65535 {
		return fmt.Errorf("SMTP端口号无效")
	}
	if c.Email.Username == "" {
		return fmt.Errorf("未配置SMTP用户名")
	}
	if c.Email.Password == "" {
		return fmt.Errorf("未配置SMTP密码")
	}
	if c.Email.From == "" {
		return fmt.Errorf("未配置发件人地址")
	}
	if len(c.Email.To) == 0 {
		return fmt.Errorf("未配置收件人地址")
	}

	return nil
}

func LoadConfig(configPath string) (*Config, error) {
	// 检查配置文件是否存在
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		return nil, fmt.Errorf("配置文件 %s 不存在", configPath)
	}

	config := &Config{}

	// 读取配置文件
	file, err := os.ReadFile(configPath)
	if err != nil {
		return nil, fmt.Errorf("读取配置文件失败: %v", err)
	}

	// 解析YAML
	if err := yaml.Unmarshal(file, config); err != nil {
		return nil, fmt.Errorf("解析配置文件失败: %v", err)
	}

	// 验证配置
	if err := config.Validate(); err != nil {
		return nil, fmt.Errorf("配置验证失败: %v", err)
	}

	return config, nil
}
